
// Force browser to display vertical scroll.
// This is done in JS instead of in CSS because some browsers (i.e. Konquerer) do not properly support styling the <html> element.
if (document.documentElement) document.documentElement.style.overflowY = 'scroll'; 

if (typeof SudokuUI === 'undefined') var SudokuUI = {};

// working grids
// even though there is only one working grid, two variables are used to store the data
// clueGrid to store the untouched grid for any revert
// progressGrid to store the current user progress
SudokuUI.clueGrid = null; // Clue matrix
SudokuUI.progressGrid = null; // User progress

SudokuUI.controller =
{
	// indicate whether any user progress was made on the grid
	hasGridChanged : false,

	// Determine whether user is editing the clues or trying to fill-in the grid
	editingMode : 'clues',

	// Determines what is displayed in an empty cell. Available values: none, calculated_candidates, notations
	notationSource : 'calculated_candidates',

	// Mask bits are ordered 987654321.
	notationFilter : 511,
	highlightFilter : 0,

	// Refresh every cell
	// After a batch cell change
	refreshDisplay : function ()
	{
		switch (SudokuUI.controller.notationSource)
		{
			case 'calculated_candidates':
				for (var i = 0; i < 9; i++)
				{
					RenderTally_Notation(i, i);
					for (var j = 0; j < 9; j++)
						RenderCell(i, j, RenderCell_TalliedCandidates);
				}
				break;

			case 'notations':
				for (var i = 0; i < 9; i++)
				{
					for (var j = 0; j < 9; j++)
						RenderCell(i, j, RenderCell_Notations);
				}
				SudokuUI.table.hideAllTallies();
				break;

			default:
				for (var i = 0; i < 9; i++)
				{
					for (var j = 0; j < 9; j++)
						RenderCell(i, j);
				}
				SudokuUI.table.hideAllTallies();
		}
	},

	// Responsible for getting the value and calculating the color 
	refreshPeers : function (row, col)
	{
		switch (SudokuUI.controller.notationSource)
		{
			case 'calculated_candidates':
				RenderCell (row, col, RenderCell_TalliedCandidates);
				CallPeers(row, col, RenderCell, RenderCell_TalliedCandidates);
				RenderTally_Notation(row, col);
				break;

			case 'notations':
				RenderCell (row, col, RenderCell_Notations);
				break;

			default:
				RenderCell(row, col);
		}
	},

	// highlight a certain number in the table
	setHighlight : function (value)
	{
		SudokuUI.controller.highlightFilter = parseInt(value);
		SudokuUI.controller.refreshDisplay();
	},

	// change the on/off state of each notationFilter
	setFilter : function (value, state)
	{
		if (state)
			SudokuUI.controller.notationFilter |= parseInt(value); 
		else 
			SudokuUI.controller.notationFilter &= ~parseInt(value);
		SudokuUI.controller.refreshDisplay();
	},

	// set a all filters at once
	setAllFilters : function (value)
	{
		var list = document.getElementsByName('notation_filter');
		for (var i = 0; i < 9; i++)
		{
			if (list[i].type === 'checkbox')
			{
				list[i].checked = value;
			}
		}
		if (value)
			SudokuUI.controller.notationFilter = 511;
		else
			SudokuUI.controller.notationFilter = 0;

		SudokuUI.controller.refreshDisplay();
	},

	// set the source of the notations on display
	setNotationSrc : function (mode)
	{
		var NotationCheckboxes = document.getElementsByName('notations_type');
		for (var i = 0; i < NotationCheckboxes.length; i++)
		{
			if (NotationCheckboxes[i].value === mode)
			{
				NotationCheckboxes[i].checked = true;
				break;
			}
		}
		SudokuUI.controller.notationSource = mode;
		SudokuUI.controller.refreshDisplay();
	},

	loadPuzzle : function (s)
	{
		SudokuUI.textIO.hideDisplay();

		if (SudokuUI.controller.hasGridChanged)
		{
			if (!confirm ('You have made changes to the current grid. Are you sure you want to load a new grid?'))
				return;
		}

		SudokuUI.controller.clearGrids();

		if (!SudokuUI.progressGrid.loadFromString(s))
		{
			alert('Failed to load from string, please make sure empty cells are represented by distinct characters.');
			return;
		}

		// check if there is cell conflict
		if (SudokuUI.progressGrid.gridHasConflict()) 
		{
			SudokuUI.clueGrid = new SudokuGrid();
			SudokuUI.clueGrid.enforcement = 'assertive';
			alert('Loaded puzzle has conflicting cells. Please resolve this conflict before attempting to solve it.');
			SudokuUI.controller.hasGridChanged = false;
			SudokuUI.controller.refreshDisplay();
			return;
		}

		SudokuUI.clueGrid = new SudokuGrid(SudokuUI.progressGrid);
		SudokuUI.clueGrid.enforcement = 'assertive';
		SudokuUI.controller.hasGridChanged = false;
		SwitchEditMode('values');
		SudokuUI.controller.setNotationSrc('calculated_candidates');
		SudokuUI.controller.refreshDisplay();
	},

	clearGrids : function ()
	{
		SudokuUI.clueGrid = null;
		SudokuUI.progressGrid = null;

		SudokuUI.clueGrid = new SudokuGrid();
		SudokuUI.clueGrid.enforcement = 'assertive';
		SudokuUI.progressGrid = new SudokuGrid();
	},

	// Simply reloads the page
	reloadPage : function ()
	{
		if(!SudokuUI.controller.hasGridChanged || confirm('You have made changes to the current grid. Are you sure you want to start from a new grid?'))
		{
			SwitchEditMode('clues');
			document.location.reload();
		}
	},

	init : function ()
	{
		if (!yux.compat.assert(document.createTextNode, 'Your browser is too old, it does not support W3C DOM Level 1.')) return;

		// Unsuppress error
		// Unsuppress function returns true if error was caught during the suppression period
		// For this project error during loading usually only occur on IE4.
		if (yux.errors.unsuppress())
		{
			alert('Unexpected error occured during loading.');
			return false;
		}

		SudokuUI.controller.clearGrids();

		SudokuUI.table.init();
		SudokuUI.input.init();
		SudokuUI.controller.refreshDisplay();
		SudokuUI.cookieMgt.render();
		SudokuUI.presets.init();

		SudokuUI.log.init();

		// Load a default grid on the first visit so user is not greeded with an empty grid.
		if (!yux.cookies.get('last_visit'))
			SudokuUI.controller.loadPuzzle('850002400720000009004000000000107002305000900040000000000080070017000000000036040');
		yux.cookies.set('last_visit', new Date());
	}

}

function RenderCell (row, col, notation_handler)
{
	// clue cells
	if (!SudokuUI.clueGrid.isEmpty (row, col))
	{
		RenderCell_Clue (row, col);
	}

	// filled cells
	else if (!SudokuUI.progressGrid.isEmpty(row, col))
	{
		RenderCell_Filled (row, col);
	}
	
	// 
	else if (notation_handler)
	{
		notation_handler (row, col);
	}

	else
	{
		SudokuUI.table.setCellBlank(row, col);
	}
}

// renders a cell with clue
function RenderCell_Clue (row, col)
{
	var value = SudokuUI.clueGrid.data[row][col];
	SudokuUI.table.setCellValue(row, col, value);

	SudokuUI.table.applyStyle(row, col, 'clue');

	if (value & SudokuUI.controller.highlightFilter)
		SudokuUI.table.applyStyle(row, col, 'highlight_strong');
}

// renders a filled cell
function RenderCell_Filled(row, col)
{
	SudokuUI.table.setCellValue(row, col, SudokuUI.progressGrid.data[row][col]);

	if (SudokuUI.controller.highlightFilter)
	{
		if (SudokuUI.progressGrid.notations[row][col] & SudokuUI.controller.highlightFilter)
			SudokuUI.table.applyStyle(row, col, 'highlight_weak');
	}
	else
	{
		if (SudokuUI.progressGrid.cellValueHasConflict(row, col))
			SudokuUI.table.applyStyle(row, col, 'highlight_bad');
	}
}

// renders a cell with user notations
function RenderCell_Notations(row, col)
{
	var value = SudokuUI.progressGrid.notations[row][col];
	SudokuUI.table.setCellNotation(row, col, SudokuUI.controller.notationFilter & value);

	if (SudokuUI.controller.highlightFilter)
	{
		if (value & SudokuUI.controller.highlightFilter)
			SudokuUI.table.applyStyle(row, col, 'highlight_weak');
	}
	else
	{
		if (value === 0)
			SudokuUI.table.applyStyle(row, col, 'highlight_bad');
		else if (value.isPowerOfTwo())
			SudokuUI.table.applyStyle(row, col, 'highlight_good');
	}
}

// renders a cell using tallied candidates
function RenderCell_TalliedCandidates(row, col)
{
	var value = SudokuUI.progressGrid.tallyCandidates(row, col);
	SudokuUI.table.setCellNotation(row, col, SudokuUI.controller.notationFilter & value);

	if (SudokuUI.controller.highlightFilter)
	{
		if (value & SudokuUI.controller.highlightFilter)
			SudokuUI.table.applyStyle(row, col, 'highlight_weak');
	}
	else
	{
		if (value === 0)
			SudokuUI.table.applyStyle(row, col, 'highlight_bad');
		else if (value.isPowerOfTwo())
			SudokuUI.table.applyStyle(row, col, 'highlight_good');
	}
}

// update the combined notations on the side. 
// Run after a change update.
function RenderTally_Notation(row, col)
{
	SudokuUI.table.setRowTally(row, SudokuUI.controller.notationFilter & SudokuUI.progressGrid.tallyRow(row));
	SudokuUI.table.setColTally(col, SudokuUI.controller.notationFilter & SudokuUI.progressGrid.tallyCol(col));
}

// setDigit + recalculation, called by user invoked actions.
function FillCellValue(row, col, value)
{
	//alert(row + ' ' + col + ' ' + value + ' ' + SudokuUI.controller.editingMode);
	switch(SudokuUI.controller.editingMode)
	{
		case 'clues':
			if (!SudokuUI.clueGrid.setDigit(row, col, value))
			{
				alert('The number ' + value + ' cannot go there. Please enter a valid clue value.');
				return
			}
			temp = SudokuUI.progressGrid.enforcement;
			SudokuUI.progressGrid.enforcement = 'assertive';
			SudokuUI.progressGrid.setDigit(row, col, value)
			SudokuUI.progressGrid.enforcement = temp;

			SudokuUI.controller.hasGridChanged = true;
			break;

		case 'values':
			if (!SudokuUI.clueGrid.isEmpty(row, col)) return;
			if (!SudokuUI.progressGrid.setDigit(row, col, value))
			{
				alert('The number ' + value + ' cannot go there. Please enter a valid candidate.');
				return
			}
			SudokuUI.controller.hasGridChanged = true;
			break;

		// UNIMPLEMENTED
		case 'notations':
			if (value > 0)
				SudokuUI.progressGrid.notations[row][col] ^= (1 << (value - 1));
			else
				SudokuUI.progressGrid.notations[row][col] = 511;
			SudokuUI.controller.hasGridChanged = true;
			break;
	}
	//RefreshPeers (row, col);
	SudokuUI.controller.refreshDisplay();
}

function SwitchEditMode(mode)
{
	SudokuUI.controller.editingMode = mode;
	//alert(row + ' ' + col + ' ' + value);

	var EditList = document.getElementsByName('edit_mode');
	
	for (var i = 0; i < EditList.length; i++)
	{
		if (EditList[i].value === mode)
		{
			EditList[i].checked = true;
			break;
		}
	}

	switch(SudokuUI.controller.editingMode)
	{
		case 'clues':
			SudokuUI.controller.setNotationSrc('calculated_candidates');
			break;
		case 'notations':
			SudokuUI.controller.setNotationSrc('notations');
			break;
		default:
			break;
	}
}

// Scroll through candidates using mouse wheel.
function ScrollThruPossibles(row, col, sign)
{
	var NewValue;
	if (SudokuUI.controller.editingMode === 'values')
	{
		if (!SudokuUI.clueGrid.isEmpty(row, col)) return;
		var ScrollList = '0' + SudokuUI.progressGrid.tallyCandidates(row, col, true).bitmaskToString();
		var loc = ScrollList.indexOf(SudokuUI.progressGrid.getDigit(row, col));
	}
	else
	{
		var ScrollList = '0' + SudokuUI.clueGrid.tallyCandidates(row, col, true).bitmaskToString();
		var loc = ScrollList.indexOf(SudokuUI.clueGrid.getDigit(row, col));
	}

	if (sign > 0)
		NewValue = ScrollList.charAt((loc + 1) % ScrollList.length);
	else
		NewValue = ScrollList.charAt((loc - 1 + ScrollList.length) % ScrollList.length);

	//console.log('Scroll wheel; Candidates: ' + ScrollList + '; Current: ' + NewValue);
	FillCellValue(row, col, parseInt(NewValue));
}

if (window.addEventListener)
	window.addEventListener('load', SudokuUI.controller.init, false);
else if (window.attachEvent)
	window.attachEvent('onload', SudokuUI.controller.init);
else
	window.onload = SudokuUI.controller.init;
